/**
 * 欧拉角 - L5
 * @author lonphy
 * @version 0.1
 */
(function(L5) {
	"use strict";

	function initConstructor() {
		/**
		 * 欧拉角构造
		 * @param heading {Number}
		 * @param pitch {Number}
		 * @param bank {Number}
		 * @constructor
		 */
		L5.Euler = function(heading, pitch, bank) {
			heading = heading || 0;
			pitch = pitch || 0;
			bank = bank || 0;
			this.content = new Float32Array([heading, pitch, bank]);
		};
	}
	function initMethods() {
		var $fn = L5.Euler.prototype;
		/**
		 * 克隆
		 * @returns {L5.Euler}
		 */
		$fn.clone = function(){
			return new L5.Euler(this.heading, this.pitch, this.bank);
		};
		/**
		 * 判断是否相等
		 * @param euler {L5.Euler} 目标欧拉角
		 * @return {Boolean}
		 */
		$fn.equals = function(euler) {
			return this.heading === euler.heading && this.pitch === euler.pith && this.bank === euler.bank;
		};
		/**
		 * 置零
		 * @returns {L5.Euler}
		 */
		$fn.identity = function() {
			this.content[0] = this.content[1] = this.content[2] = 0;
			return this;
		};
		/**
		 * 变换为限制集欧拉角
		 * @returns {L5.Euler}
		 */
		$fn.canonize = function() {
			var pitch = L5.Math.wrapPI(this.pitch),
				pi = L5.Math.PI,
				pi_2 = L5.Math.PI_HALF;
			this.pitch = pitch;
			if (pitch < -pi_2) {
				this.pitch = -pi - pitch;
				this.heading += pi;
				this.bank += pi;
			}
			else if(pitch > pi_2){
				this.pitch = pi - pitch;
				this.heading += pi;
				this.bank += pi;
			}
			// 万向锁检查
			if (Math.abs(pitch) > pi_2 - 1e-4) {
				// 万向锁时将垂直轴旋转累加到heading
				this.heading += this.bank;
				this.bank = 0;
			}
			else {
				this.bank = L5.Math.wrapPI(this.bank);
			}
			this.heading = L5.Math.wrapPI(this.heading);
			return this;
		};
		/**
		 * 从四元数转换为欧拉角[物体-惯性]
		 * @param qua {L5.Quaternion}
		 * @returns {L5.Euler}
		 */
		$fn.fromObjectToInertiaQuaternion = function(qua) {
			// 计算 sin(pitch)
			var sp = -2 * (qua.y*qua.z - qua.w*qua.x);
			// 万向锁检查
			if (Math.abs(sp) > 0.9999) {
				this.pitch = L5.Math.PI_HALF * sp;
				this.heading = Math.atan2(-qua.x*qua.z + qua.w*qua.y, -0.5 - qua.y*qua.y - qua.z*qua.z);
				this.bank = 0;
			}
			else {
				this.pitch = Math.asin(sp);
				this.heading = Math.atan2(-qua.x*qua.z + qua.w*qua.y, 0.5 - qua.x*qua.x - qua.y*qua.y);
				this.bank = Math.atan2(-qua.x*qua.y + qua.w*qua.z, 0.5 - qua.x*qua.x - qua.z*qua.z);
			}
			return this;
		};
		/**
		 * 从四元数转换为欧拉角[惯性-物体]
		 * @param qua {L5.Quaternion}
		 * @returns {L5.Euler}
		 */
		$fn.fromInertiaToObjectQuaternion = function(qua) {
			// 计算 sin(pitch)
			var sp = -2 * (qua.y*qua.z + qua.w*qua.x);
			// 万向锁检查
			if (Math.abs(sp) > 0.9999) {
				this.pitch = L5.Math.PI_HALF * sp;
				this.heading = Math.atan2(-qua.x*qua.z + qua.w*qua.y, 0.5 - qua.y*qua.y - qua.z*qua.z);
				this.bank = 0;
			}
			else {
				this.pitch = Math.asin(sp);
				this.heading = Math.atan2(qua.x*qua.z - qua.w*qua.y, 0.5 - qua.x*qua.x - qua.y*qua.y);
				this.bank = Math.atan2(qua.x*qua.y - qua.w*qua.z, 0.5 - qua.x*qua.x - qua.z*qua.z);
			}
			return this;
		};
		/**
		 * 从矩阵转换为欧拉角[物体-世界]
		 * @returns {L5.Euler}
		 */
		$fn.fromObjectToWorldMat = function(mat) {
			var sp = -mat.m32;
			// 检查万向锁
			if (Math.abs(sp) > 9.99999) {
				this.pitch = L5.Math.PI_HALF * sp;
				this.heading = Math.atan2(-mat.m23, mat.m11);
				this.bank = 0;
			}
			else {
				this.heading = Math.atan2(mat.m31, mat.m33);
				this.pitch = Math.asin(sp);
				this.bank = Math.atan2(mat.m12, mat.m22);
			}
			return this;
		};
		/**
		 * 从矩阵转换为欧拉角[世界-物体]
		 * @returns {L5.Euler}
		 */
		$fn.fromWorldToObjectMat = function(mat) {
			var sp = -mat.m23;
			// 检查万向锁
			if (Math.abs(sp) > 9.99999) {
				this.pitch = L5.Math.PI_HALF * sp;
				this.heading = Math.atan2(-mat.m31, mat.m11);
				this.bank = 0;
			}
			else {
				this.heading = Math.atan2(mat.m13, mat.m33);
				this.pitch = Math.asin(sp);
				this.bank = Math.atan2(mat.m21, mat.m22);
			}
			return this;
		};
		/**
		 * 从旋转矩阵转换到欧拉角
		 * @param rm {L5.RotateMatrix} 旋转矩阵
		 * @returns {L5.Euler}
		 */
		$fn.fromRotateMatrix = function(rm) {
			var sp = -rm.m23;
			// 检查万向锁
			if (Math.abs(sp) > 9.99999) {
				this.pitch = L5.Math.PI_HALF * sp;
				this.heading = Math.atan2(-rm.m31, rm.m11);
				this.bank = 0;
			}
			else {
				this.heading = Math.atan2(rm.m13, rm.m33);
				this.pitch = Math.asin(sp);
				this.bank = Math.atan2(rm.m21, rm.m22);
			}
			return this;
		};
		/**
		 * 打印
		 */
		$fn.print = function() {
			console.log(this.toString());
		};
		/**
		 * toString复写
		 * @returns {string}
		 */
		$fn.toString = function() {
			var out = 'Euler[';
			out += this.content[0].toFixed(8, 10) + ' ';
			out += this.content[1].toFixed(8, 10) + ' ';
			out += this.content[2].toFixed(8, 10) + ']';
			return out;
		};
	}
	function setupAlias() {
		var $fn = L5.Euler.prototype,
			indexOf0Accessor = {
				get: function () { return this.content[0]; },
				set: function (val) { this.content[0] = val; }
			},
			indexOf1Accessor = {
				get: function () { return this.content[1]; },
				set: function (val) { this.content[1] = val; }
			},
			indexOf2Accessor = {
				get: function () { return this.content[2]; },
				set: function (val) { this.content[2] = val; }
			};
		// 设置heading-pitch-bank访问器
		L5.define(true, $fn, 'heading', indexOf0Accessor);
		L5.define(true, $fn, 'pitch', indexOf1Accessor);
		L5.define(true, $fn, 'bank', indexOf2Accessor);
	}
	initConstructor();
	initMethods();
	setupAlias();
})(window.L5);
